// rgScene.cpp
#include <base/Chronometer.h>
#include <graphics/rgScene.h>


namespace 
{
	bool triIntersect(
		const rgRay& inRay, rgReal& distance, 
		const rgPoint& t0, const rgPoint& t1, const rgPoint& t2)
	{
		const rgVector& edge1 = t1 - t0;
		const rgVector& edge2 = t2 - t0;
		const rgVector& pVec = inRay.m_direction ^ edge2;
		rgReal det, t, u, v;

		det = edge1 * pVec;

		// ray is parallel to plane or comes from the back
		if (det < EPSILON) return rgFALSE;

		const rgVector tVec = inRay.m_origin - t0;
		u = tVec * pVec;
		if (u < static_cast<rgReal> (0) || u > det) return rgFALSE;

		const rgVector qVec =  tVec ^ edge1;
		v = inRay.m_direction * qVec;
		if (v < static_cast<rgReal> (0) || u + v > det) return rgFALSE;

		t = edge2 * qVec;
		distance = t / det;
		
		return rgTRUE;
	}
}

bool rg::scene::closestObject( rgRay& inRay, rgPoint& hitPoint, rgNormal& n, rgReal& distance, material*& mat )
{
	rgBool ret = rgFALSE;
	//static rgBool firstTime = true;
	//static Chronometer* closestChrono = 0;
	//static Chronometer* sphereChrono = 0;
	//static Chronometer* triChrono = 0;
	//static Chronometer* normalChrono = 0;

	//if (firstTime)
	//{
	//	firstTime = false;
	//	closestChrono = Chronos::Singleton().get("closest: ");
	//	sphereChrono =  Chronos::Singleton().get("sphere: ");
	//	triChrono =  Chronos::Singleton().get("tri: ");
	//	normalChrono =  Chronos::Singleton().get("normal: ");
	//}

	//closestChrono->start();

	m_hitSphere = 0;
	lastSortestDistance = INFINITE;
	rgUint x;
	
	//sphereChrono->start();
	const size_t sphereSize = m_sphereList.size();
	for( x = 0; x < sphereSize; ++x )
	{
		if ( (m_sphereList[x]->intersect(inRay, distance)) && (distance < lastSortestDistance) )
		{
			lastSortestDistance = distance;
			m_hitSphere = m_sphereList[x];
			mat = m_hitSphere->m_pMaterial;
		}
	}
	//sphereChrono->lapse();

	
	//triChrono->start();
	//m_hitTriangle = 0;
	const rg::triangle* closestTri = 0;
	const unsigned triSize = m_triangleList.size();
	for( x = 0; x < triSize; ++x )
	{
		const rg::triangle* tri = m_triangleList[x];

		//if ( (m_triangleList[x]->intersect(inRay, distance)) && (distance < lastSortestDistance) )
		if ( triIntersect(inRay, distance, tri->m_vertex[0],tri->m_vertex[1],tri->m_vertex[2] ) &&
			 distance < lastSortestDistance )
		{
			lastSortestDistance = distance;
			closestTri=tri;
			//m_hitTriangle = m_triangleList[x];
			//mat = m_hitTriangle->m_pMaterial;
		}
	}
	//triChrono->lapse();

	//normalChrono->start();
	hitPoint = inRay(lastSortestDistance);

	if (closestTri)
	{
		mat = closestTri->m_pMaterial;
		n.set(closestTri->m_normal.x, closestTri->m_normal.y, closestTri->m_normal.z);
		ret=rgTRUE;
	}
	else 
	if(m_hitSphere)
	{
		m_hitSphere->getNormal(hitPoint, n);
		ret=rgTRUE;
	}
	//normalChrono->lapse();

	//closestChrono->lapse();
	
	return ret;
}